{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e352c272-9316-478d-8959-3cd643349329",
   "metadata": {},
   "source": [
    "Chapter 07\n",
    "\n",
    "# 初等行变换求逆矩阵\n",
    "《线性代数》 | 鸢尾花书：数学不难"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "447d4e7d-3744-42c8-b514-4f3d2795ee7d",
   "metadata": {},
   "source": [
    "该代码实现了 **利用初等行变换计算方阵的逆矩阵**，其核心思想基于**高斯-约当消元法** (Gauss-Jordan elimination)。我们希望找到一个矩阵 $A$ 的逆矩阵 $A^{-1}$，即满足：\n",
    "$$\n",
    "A A^{-1} = I\n",
    "$$\n",
    "其中 $I$ 是单位矩阵。\n",
    "\n",
    "---\n",
    "\n",
    "### **步骤 1：检查矩阵是否可逆**\n",
    "首先，我们确保 $A$ 是一个方阵，即 $A$ 的行数 $n$ 和列数 $m$ 相等：\n",
    "$$\n",
    "A \\in \\mathbb{R}^{n \\times n}\n",
    "$$\n",
    "如果 $A$ 不是方阵，则它没有逆矩阵，直接返回错误。\n",
    "\n",
    "然后，我们计算 $A$ 的行列式：\n",
    "$$\n",
    "\\det(A)\n",
    "$$\n",
    "如果 $\\det(A) = 0$，那么 $A$ 是**奇异矩阵 (singular matrix)**，即 $A$ 不可逆。因此，如果 $\\det(A) = 0$，则返回错误。\n",
    "\n",
    "---\n",
    "\n",
    "### **步骤 2：构造增广矩阵**\n",
    "为了计算 $A^{-1}$，我们构造一个增广矩阵，将单位矩阵 $I$ 附加到 $A$ 的右侧：\n",
    "$$\n",
    "[A | I] = \\begin{bmatrix} A & I \\end{bmatrix} \\in \\mathbb{R}^{n \\times 2n}\n",
    "$$\n",
    "例如，对于一个 $3 \\times 3$ 矩阵：\n",
    "$$\n",
    "\\left[ \\begin{array}{ccc|ccc}\n",
    "1 & 3 & 6 & 1 & 0 & 0 \\\\\n",
    "2 & 7 & 14 & 0 & 1 & 0 \\\\\n",
    "0 & 2 & 5 & 0 & 0 & 1\n",
    "\\end{array} \\right]\n",
    "$$\n",
    "目标是通过行变换把左侧的 $A$ 变为单位矩阵 $I$，这样右侧部分就变成了 $A^{-1}$。\n",
    "\n",
    "---\n",
    "\n",
    "### **步骤 3：高斯-约当消元法**\n",
    "高斯-约当消元法的目标是**逐步将矩阵转换为单位矩阵**，其核心包括三个操作：\n",
    "\n",
    "1. **选取主元并交换行**：\n",
    "   - 在当前列中，选择**绝对值最大**的元素作为主元，并交换该行与当前行。\n",
    "   - 这一操作保证主元尽可能远离零，以避免数值计算中的不稳定性。\n",
    "\n",
    "2. **归一化主元**：\n",
    "   - 通过行数乘法，将主元归一化为 1：\n",
    "   $$\n",
    "   A_{ii} \\leftarrow \\frac{A_{ii}}{A_{ii}}\n",
    "   $$\n",
    "   - 这意味着当前行的所有元素都会除以主元。\n",
    "\n",
    "3. **消去其他行的该列元素**：\n",
    "   - 对于矩阵中的每一行 $j \\neq i$，进行消元：\n",
    "   $$\n",
    "   A_{j} \\leftarrow A_{j} - A_{ji} \\cdot A_i\n",
    "   $$\n",
    "   - 这个操作确保主元列中除了主元外的其他元素都变成 $0$。\n",
    "\n",
    "经过这三个步骤，最终 $A$ 的位置变成单位矩阵 $I$，而单位矩阵 $I$ 的位置变成 $A^{-1}$。\n",
    "\n",
    "---\n",
    "\n",
    "### **步骤 4：提取逆矩阵**\n",
    "在增广矩阵 $[I | A^{-1}]$ 形成后，我们从右侧提取逆矩阵：\n",
    "$$\n",
    "A^{-1} = A_I[:, n:]\n",
    "$$\n",
    "这样就得到了 $A$ 的逆矩阵。\n",
    "\n",
    "---\n",
    "\n",
    "### **总结**\n",
    "- **时间复杂度**：该算法的时间复杂度为 $O(n^3)$，与高斯消元法相同。\n",
    "- **数值稳定性**：通过选取主元的策略减少数值不稳定性（如除零问题）。\n",
    "- **适用性**：仅适用于**可逆方阵**，如果 $A$ 是奇异矩阵，则无法求逆。\n",
    "\n",
    "最终，该代码实现了一种**基于行变换的矩阵求逆方法**，能够将任意可逆方阵转换为其逆矩阵。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83bef096-8c5e-485b-bd7a-49bcaf74270a",
   "metadata": {},
   "source": [
    "## 初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a78ac09f-d90f-42c7-b670-d9532e91bbba",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e341c8b3-8b24-46ca-81f1-18dda1e984de",
   "metadata": {},
   "source": [
    "## 自定义函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "14f62ad7-542a-4e78-bfa1-9a59b2061014",
   "metadata": {},
   "outputs": [],
   "source": [
    "def inverse_matrix(A):\n",
    "\n",
    "    # 确保 A 是 NumPy 数组并转换为浮点数类型\n",
    "    A = np.array(A, dtype=float)\n",
    "    \n",
    "    # 获取矩阵的行数和列数\n",
    "    n, m = A.shape\n",
    "\n",
    "    # 判断是否为方阵\n",
    "    if n != m:\n",
    "        print(\"错误：矩阵不是方阵，无法求逆。\")\n",
    "        return None\n",
    "\n",
    "    # 计算行列式，判断是否可逆\n",
    "    if np.linalg.det(A) == 0:\n",
    "        print(\"错误：矩阵的行列式为 0，无法求逆。\")\n",
    "        return None\n",
    "\n",
    "    # 创建单位矩阵 I\n",
    "    I = np.eye(n)\n",
    "\n",
    "    # 构造增广矩阵 [A | I]\n",
    "    A_I = np.hstack((A, I))\n",
    "\n",
    "    # 进行高斯-约当消元法\n",
    "    for i in range(n):\n",
    "        # 1) 选取主元（最大值交换行）\n",
    "        max_row = np.argmax(np.abs(A_I[i:, i])) + i\n",
    "        A_I[[i, max_row]] = A_I[[max_row, i]]\n",
    "\n",
    "        # 2) 归一化主元（主对角线元素变为 1）\n",
    "        A_I[i] = A_I[i] / A_I[i, i]\n",
    "\n",
    "        # 3) 消元（使主元列的其他元素变为 0）\n",
    "        for j in range(n):\n",
    "            if i != j:\n",
    "                A_I[j] -= A_I[j, i] * A_I[i]\n",
    "\n",
    "    # 提取逆矩阵\n",
    "    A_inv = A_I[:, n:]\n",
    "\n",
    "    return A_inv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "548ceb7b-7c62-46d3-bd12-e3ac5e224e20",
   "metadata": {},
   "source": [
    "## 定义矩阵 A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "592dca4c-f76c-4ef1-b666-0d4373c319cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "A = np.array([[1, 3, 6], \n",
    "              [2, 7, 14],\n",
    "              [0, 2, 5]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4362cdb-6026-4948-ace8-66cf51e33ce3",
   "metadata": {},
   "source": [
    "## 计算逆矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ed4ed750-c5d7-4485-88ce-e64b3027f496",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  7.,  -3.,   0.],\n",
       "       [-10.,   5.,  -2.],\n",
       "       [  4.,  -2.,   1.]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_inv = inverse_matrix(A)\n",
    "A_inv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b0310c8-e5c6-4cf3-b912-a175a9fc6077",
   "metadata": {},
   "source": [
    "## 回顾求解线性方程组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ee8dbefa-9c64-4faf-afbf-41b21829fe74",
   "metadata": {},
   "outputs": [],
   "source": [
    "b = np.array([[25], \n",
    "              [58], \n",
    "              [19]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "37f8c612-eb8f-4a84-812d-ecd71f0e6156",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.],\n",
       "       [2.],\n",
       "       [3.]])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A_inv @ b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ea4c06f-5ec2-4aa9-8255-dbe5c964dafa",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "16650792-deab-4d0b-a223-2a6b7f41a5e1",
   "metadata": {},
   "source": [
    "作者\t**生姜DrGinger**  \n",
    "脚本\t**生姜DrGinger**  \n",
    "视频\t**崔崔CuiCui**  \n",
    "开源资源\t[**GitHub**](https://github.com/Visualize-ML)  \n",
    "平台\t[**油管**](https://www.youtube.com/@DrGinger_Jiang)\t\t\n",
    "\t\t[**iris小课堂**](https://space.bilibili.com/3546865719052873)\t\t\n",
    "\t\t[**生姜DrGinger**](https://space.bilibili.com/513194466)  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
